package emediashared.model;

import java.io.File;
import java.net.InetAddress;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchListener;
import org.eclipse.ui.PlatformUI;

import emediashared.SharedActivator;
import emediashared.ui.PreferencesDialog;


public class RmiSharedServer extends java.rmi.server.UnicastRemoteObject implements IRemoteMessageTransfer, ISharedServer{

	private static final int PORT = 3232;
	private static final long serialVersionUID = 1L;
	private String userName;
	private String address;
	private int port;
	private Registry registry;
	private static final int MAX_RETRIES = 3; 
	private int retries = MAX_RETRIES;
	
	private static ISharedServer instance;
	private boolean connected;
	
	@Override
	public Object execute(ClientRequest request, IRemoteProgressMonitor remoteProgressMonitor, String... params) throws RemoteException {
		remoteProgressMonitor = RemoteProgressMonitor.getNonNullMonitor(remoteProgressMonitor);
		switch (request) {
		case GET_FILES_LIST:
			String directory = params != null ? params[0] : null;
           return getFiles(directory);
		case DOWNLOAD_FILE:
			try {
				return copyFiles(params[0], params[1], remoteProgressMonitor);
			} catch (Exception e) {
				throw new RemoteException(e.getMessage(), e);
			}
		case DELETE_FILE :
		    return deleteFile(params[0]);		
		default:
			break;
		}
		return null;
	}
	
	private boolean deleteFile(String filePath) {
		return new File(filePath).delete();
	}

	private File copyFiles(String sourceFile, String targetDir, IRemoteProgressMonitor remoteProgressMonitor) throws Exception {
		return RemoteFile.copy(sourceFile, targetDir, remoteProgressMonitor);
	}

	private List<IRemoteFile> getFiles(String path) throws RemoteException {
		List<IRemoteFile> files = new ArrayList<IRemoteFile>();
		if (path == null) {
			String localPath = PreferencesDialog.getLocalPath();
			String[] split = localPath.split(SharedConstants.PATH_DELIMITTER);
			for (String string : split) {
				files.add(new RemoteFile(string, true));
			}
		} else {
			File parent = new File(path);
			if (parent.exists()) {
				for (File child : parent.listFiles()) {
					RemoteFile remoteFile = new RemoteFile(child.getAbsolutePath(), child.isDirectory());
					files.add(remoteFile);
				}
			}
		}
		return files;
	}

	public static ISharedServer getInstance() {
		try {
			if (instance == null) {
				instance = new RmiSharedServer();
			}
			if (!instance.isConnected()) {
				instance.reconnect();
			}
		} catch (Exception e) {
			SharedActivator.showAndLogError(e);
		}
		return instance;
	}
	
	private boolean sampleClient;
	private InetAddress localHost;
	
	{
		try {
			ResourcesPlugin.getWorkspace();
		} catch (IllegalStateException e) {
			sampleClient = true;
		}
	}
	
	private RmiSharedServer() throws RemoteException{
	}
	
	/* (non-Javadoc)
	 * @see emediashared.model.ISharedServer#isConnected()
	 */
	@Override
	public boolean isConnected() {
		return connected;
	}

	private boolean connect() {
		if (sampleClient) {
			userName = "Prasad";
		} else {
			userName = PreferencesDialog.getUserName();
		}
		
		try {
			localHost = InetAddress.getLocalHost();
			if (localHost.isLoopbackAddress()) {
				System.out.println("Loopback=" + localHost.isLoopbackAddress());
				if (sampleClient) {
					// retry mechanism
					int sleepTime = sampleClient ? 1000 : 100000;
					Thread.sleep(sleepTime);
					reconnect();
					if (connected) {
						return true;
					}
				} else {
					return false;
				}
			}
			address = localHost.getHostAddress();
		} catch (Exception e) {
			System.out.println("can't get inet address.");
		}
		port = PORT;
		retries = MAX_RETRIES;
		while (retries-- > 0) {
			try {
				registry = LocateRegistry.createRegistry(port);
				registry.rebind(userName, this);
				if (!sampleClient) {
					PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() {
						@Override
						public boolean preShutdown(IWorkbench workbench, boolean forced) {
							try {
								registry.unbind(userName);
							} catch (Exception e) {
								SharedActivator.showAndLogError(e);
							}
							return true;
						}
						
						@Override
						public void postShutdown(IWorkbench workbench) {
							
						}
					});
				}
				System.out.println("Listening@" + address + ",port=" + port);
				retries = 0;
				connected = true;
			} catch (RemoteException e) {
				SharedActivator.showAndLogError("Failed", "Failed to connect to port:" + port, e);
				port = port + MAX_RETRIES - retries;
			}
		}
		return connected;
	}

	/* (non-Javadoc)
	 * @see emediashared.model.ISharedServer#reconnect()
	 */
	@Override
	public void reconnect() throws Exception {
		if (!connected) {
			connect();
		} else {
			// remove old 
			registry.unbind(userName);
			userName = PreferencesDialog.getUserName();
			registry.rebind(userName, this);
		}
	}
	
	public int getPort() {
		return port;
	}
	
	/* (non-Javadoc)
	 * @see emediashared.model.ISharedServer#getAddress()
	 */
	@Override
	public String getAddress() {
		return address;
	}
	
	/* (non-Javadoc)
	 * @see emediashared.model.ISharedServer#getUrl()
	 */
	@Override
	public String getUrl() {
       return address + SharedRmiClient.PORT_SEPARATOR + port;
	}

	public static void main(String[] args) {
		RmiSharedServer.getInstance();
	}
	
}
